﻿using System;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using iTextSharp.text.pdf; //nugget: this thing makes PDF form filling absolutely trivial, love it, see PdfReaderand & PdfStamper usage below
using iTRAACv2.Model;

// ReSharper disable CheckNamespace
namespace iTRAACv2
// ReSharper restore CheckNamespace
{
  public class ReasonConfirmationArgs : EventArgs
  {
    public string Reason { get; set; }
    public bool Accept { get; set; }
  }

// ReSharper disable InconsistentNaming
  public static class iTRAACHelpers
// ReSharper restore InconsistentNaming
  {
    static public string FiscalYear2Chars
    {
      get
      {
        return ((DateTime.Now.Year +
          ((DateTime.Now < DateTime.Parse("10/1/" + DateTime.Now.Year.ToString(CultureInfo.InvariantCulture))) ? 0 : 1)).ToString(CultureInfo.InvariantCulture).Right(2));
      }
    }

    static public bool DataColumnVisible(DataColumn column)
    {
      if (column == null) return (false);

      return(
        DataColumnVisible(column.ColumnName)
        //&& (column.Expression == "") //hide columns that were slapped on with a client side expression (assuming those are special purpose)
      );
    }

    static public bool DataColumnVisible(string columnName)
    {
      return (columnName.Right(2).ToLower() != "id" /*&& ColumnName.Right(4).ToLower() != "code"*/); //hide columns with "ID" in the name
    }

    static public void WpfDataGridStandardBehavior(DataGrid grid)
    {
      grid.ContextMenu = Application.Current.FindResource("WPFDataGrid_CopyMenu") as ContextMenu;
      grid.MouseRightButtonUp += WPFHelpers.WPFDataGridMouseRightButtonUpSaveCell;
      grid.AutoGeneratingColumn += GridAutoGeneratingColumn;
      grid.LoadingRow += GridLoadingRow;
    }

    static void GridLoadingRow(object sender, DataGridRowEventArgs e)
    {
      e.Row.Header = (e.Row.GetIndex() + 1).ToString(CultureInfo.InvariantCulture); //nugget: easy row numbers displayed in DataGrid.Row.Header (irrespective of the active sort) via grid.LoadingRow event
    }

    static private void GridAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) //nugget:
    {
      bool hide = false;
      var propertyDescriptor = e.PropertyDescriptor as PropertyDescriptor;
      var dgrid = sender as DataGrid;

      if (dgrid != null)
      {
        var dview = dgrid.ItemsSource as DataView;

        //hide column if it's used in a grouping header... 
        if (dview != null)
        {
          var bview = CollectionViewSource.GetDefaultView(dview) as BindingListCollectionView;
          if (bview != null && bview.GroupDescriptions != null && bview.GroupDescriptions.Count > 0)
          {
            //this currently hard coded to simply look at the first Grouping specification to decide so that this isn't a long delay since this event gets fired for each column
            var pg = bview.GroupDescriptions[0] as PropertyGroupDescription;
            if (pg != null && pg.PropertyName == e.PropertyName) hide = true;
          }
        }

        bool propertyIsBrowsable = propertyDescriptor != null && propertyDescriptor.IsBrowsable;
        e.Column.Visibility = ( hide 
                                || !propertyIsBrowsable //recognize the [Browsable(false)] attribute, nugget: too bad the DataGrid doesn't respect this natively!?!
                                || ((dview == null) ? !DataColumnVisible(e.PropertyName) : !DataColumnVisible(dview.Table.Columns[e.PropertyName])) //hide any columns flagged specificlaly as hidden in the model layer
                                || (dgrid.Columns.Any(c => c.SortMemberPath == e.PropertyName)) //hide any columns that are already present (handy for having a couple predefined columns (e.g. hyperlink template) on an autogenerated columns grid)
                                    //(e.PropertyDescriptor.ToString() == "System.Data.DataRelationPropertyDescriptor") ) //hide autogenerated child view columns
                              ) ? Visibility.Hidden : Visibility.Visible;
      }

      if (propertyDescriptor != null)
        e.Column.Header = propertyDescriptor.DisplayName; //nugget: support the [DisplayName("xyz")] attribute on Object Model Classes, too bad the DataGrid doesn't respect this natively!?!

      if (e.Column.Header.ToString().ToLower().Contains("date")) e.Column.Width = 70;
    }

  }

  public class PDFFormFiller
  {
    public PDFFormFiller(string pdfFileName, string uniqueId)
    {
      //nugget: where to store program data
      //nugget: http://blogs.msdn.com/b/cjacks/archive/2008/02/05/where-should-i-write-program-data-instead-of-program-files.aspx

      //currently under Win7, this path corresponds to "C:\Users\{username}\AppData\Local" 
      _filledPdfPath = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
        Path.GetFileNameWithoutExtension(pdfFileName) + "_" + uniqueId + ".pdf");

      using (var pdfStream = AssemblyHelper.GetEmbeddedResource(pdfFileName.Replace(@"\", ".")))
      {
        //nugget: iTextSharp makes PDF form filling absolutely trivial, love it
        var pdfReader = new PdfReader(pdfStream);
        _pdfStamper = new PdfStamper(pdfReader, new FileStream(_filledPdfPath, FileMode.Create));
        _formFields = _pdfStamper.AcroFields;
      }
    }

    private readonly PdfStamper _pdfStamper;
    private readonly string _filledPdfPath;
    private readonly AcroFields _formFields;

    public void SetField(string formFieldName, object value)
    {
      _formFields.SetField(formFieldName, value.ToString());
    }

    public void Display()
    {
      // flatten the form to remove editting options...
      // set it to false to leave the form open to subsequent manual edits
      //pdfStamper.FormFlattening = false;

      _pdfStamper.Close();
      _pdfStamper.Dispose();

      //fire up the PDF viewer
      var acroreader = Process.Start(_filledPdfPath);
      Debug.Assert(acroreader != null, "acroreader != null");

      //spawn a background thread to wait for the PDF Viewer (i.e. Acrobat Reader) to be closed so PDF file can be deleted... so these files don't pile up indefinitely
      //this obviously isn't foolproof... e.g. if the user closes iTRAACv2 prior to AcroReader... but it should be pretty good
      new Thread(delegate(object filepath)
      {
        try
        {
          acroreader.WaitForExit();
          File.Delete((string)filepath);
        }
        catch (Exception ex) //this exception handler must be included as part of this pattern wherever else it's implemented
        {
          System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)delegate { throw ex; }, null); //toss any exceptions over to the main UI thread, per MSDN direction: http://msdn.microsoft.com/en-us/library/system.windows.application.dispatcherunhandledexception.aspx
        }
      }).Start(_filledPdfPath);

    }

  }


// ReSharper disable InconsistentNaming
  public class iTRAACProc : Proc
// ReSharper restore InconsistentNaming
  {
    public iTRAACProc(string procName) : base(procName)
    {
      InitParms();
    }

    private void InitParms()
    {
      this["@UserGUID"] = UserModel.Current.GUID;
      this["@TaxOfficeId"] = SettingsModel.TaxOfficeId;
      this["@TaxOfficeCode"] = SettingsModel.TaxOfficeCode;
    }

    public override void ClearParms()
    {
      base.ClearParms();
      InitParms();
    }
  }

}